package com.atguigu.gmall.seckill.listener;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.order.entity.OrderInfo;
import com.atguigu.gmall.seckill.dto.SeckillQueueMsg;
import com.atguigu.gmall.seckill.entity.SeckillGoods;
import com.atguigu.gmall.seckill.service.SeckillGoodsService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class SeckillQueueListener {

    @Autowired
    private SeckillGoodsService seckillGoodsService ;

    @Autowired
    private RedisTemplate<String , String> redisTemplate ;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "seckill_order_queue" , durable = "true") ,
            exchange = @Exchange(name = "seckill.exchange" , durable = "true" , type = ExchangeTypes.DIRECT) ,
            key = { "seckill.queue" }
    ))
    public void seckillQueueListener(Message message , Channel channel) {

        // 获取消息
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        byte[] body = message.getBody();
        String msg = new String(body);
        log.info("msg: {}" , msg );

        // 数据库库存扣减
        SeckillQueueMsg seckillQueueMsg = JSON.parseObject(msg, SeckillQueueMsg.class);
        Long skuId = seckillQueueMsg.getSkuId();
        Long userId = seckillQueueMsg.getUserId();

        // redis的key
        String seckillCode = getSeckillCode(skuId , userId) ;
        String seckillOrderRedisKey = "seckill:goods:order:" + seckillCode ;

        try {

            // 进行库存的扣减(MySQL)
            seckillGoodsService.deduceStock(skuId , 1) ;

            // 如果扣减成功了，那么此时就生成订单，并且把订单数据保存到Redis中
            OrderInfo orderInfo = seckillGoodsService.prepareOrderInfo(seckillQueueMsg) ;
            redisTemplate.opsForValue().set(seckillOrderRedisKey , JSON.toJSONString(orderInfo) , 2 , TimeUnit.DAYS);

            // Redis中的库存数据进行扣减
            // 获取当前系统时间
            Date date = new Date();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd") ;
            String format = simpleDateFormat.format(date);
            String seckillGoodsRedisKey = "seckill:goods:" + format ;
            String seckillGoodsJSON = redisTemplate.opsForHash().get(seckillGoodsRedisKey, String.valueOf(skuId)).toString();
            SeckillGoods seckillGoods = JSON.parseObject(seckillGoodsJSON, SeckillGoods.class);
            seckillGoods.setStockCount(seckillGoods.getStockCount() - 1);
            redisTemplate.opsForHash().put(seckillGoodsRedisKey , String.valueOf(skuId) , JSON.toJSONString(seckillGoods));

            // 进行应答
            channel.basicAck(deliveryTag ,true);

        }catch (Exception e) {

            e.printStackTrace();

            // 如果扣减失败了，就向Redis中保存x
            redisTemplate.opsForValue().set(seckillOrderRedisKey , "x" , 2 , TimeUnit.DAYS) ;

            // 进行应答
            try {
                channel.basicAck(deliveryTag ,true);
            } catch (IOException ex) {
                ex.printStackTrace();
            }

        }

    }

    public String getSeckillCode(Long skuId , Long userId) {

        // 获取当前系统时间
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd") ;
        String format = simpleDateFormat.format(date);

        // 拼接秒杀码
        String seckillCodeStr = format + "-" + userId + "-" + skuId ;

        // 返回
        String digest = DigestUtils.md5DigestAsHex(seckillCodeStr.getBytes());
        return digest ;

    }

}
